/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package mopso;

import java.util.ArrayList;
import java.util.Arrays;

/**
 *
 * @author robson
 */
public class AdaptiveGrid {
    private int nDimensions;
    private int nGridByDimension;       
    private MappedCells[] mappedCells;
    private ArrayList<Particle> externalArchive;
    private double[] maxValue; 
    private double[] minValue; 
    private double[] sizeRange;
    
    public AdaptiveGrid() {
        
    }    

    public void calculateGrid( int nDimensions, int nGridByDimension, ArrayList<Particle> externalArchive) {
        this.nDimensions = nDimensions;
        this.nGridByDimension = nGridByDimension;
        this.externalArchive = externalArchive;
        
        int nMappedCells = (int) Math.pow( nGridByDimension, nDimensions );
        
        maxValue = new double[nDimensions];
        
        for( int k = 0; k < maxValue.length; k++ ) {
            maxValue[k] = this.getMaxValue( k, externalArchive );            
        }
        
        minValue = new double[nDimensions];
        
        for( int k = 0; k < minValue.length; k++ ) {
            minValue[k] = this.getMinValue( k, externalArchive );            
        }        
        
        sizeRange = new double[nDimensions];        
        
        for( int l = 0; l < maxValue.length; l++ ) {
            sizeRange[l] = ( maxValue[l] - minValue[l] ) / nGridByDimension;            
        }        
        
        mappedCells = new MappedCells[nMappedCells];
        
        for( int j = 0; j < mappedCells.length; j++ ) {
            mappedCells[j] = new MappedCells();
            
            mappedCells[j].createLineGridMapped( nDimensions );
        }        
        
//        lineGrid = new LineGrid[nDimensions];          
//        
//        for( int i = 0; i < lineGrid.length; i++ ) {
//            lineGrid[i] = new LineGrid();
//            
//            // colocar no lugar de 1.0 o valor máximo de cada dimensão
//            lineGrid[i].createCells( nGridByDimension, 1.0 );
//        }
        
        this.mapGridWithMappedCells( nDimensions, nGridByDimension );        
        this.countGrid();
        this.calculateFitnessDiversity();
    }

    public void countGrid() {
        int[] dimensionIndex = new int[nDimensions];
        
        for ( int i = 0; i < externalArchive.size(); i++ ) {
            for ( int j = 0; j < nDimensions; j++ ) {
                dimensionIndex[j] = this.calculateSolutionCellInDimension( externalArchive.get(i).getParticleData().getFitness()[j] ,getNGridByDimension(), minValue[j], maxValue[j]  );
                externalArchive.get(i).getParticleData().setAdaptiveGridIndexes(dimensionIndex[j], j);
            }
            
            
            this.incrementMappedCell( dimensionIndex );            
        }
    }

    private void calculateFitnessDiversity() {
        for ( int i = 0; i < mappedCells.length; i++ ) {
            if( mappedCells[i].getNSolutionsInCell() > 0 ) {
                mappedCells[i].setFitnessDiversity( 10.0 / (double) mappedCells[i].getNSolutionsInCell() );    
            }            
        }
    }

    private double getMaxValue( int k, ArrayList<Particle> externalArchive ) {
        double max = 0.0;
        
        for ( int i = 0; i < externalArchive.size(); i++ ) {
            if( externalArchive.get(i).getParticleData().getFitness()[k] > max ) {
                max = externalArchive.get(i).getParticleData().getFitness()[k];
            }
        }        
        return max;
    }
    
    private double getMinValue( int k, ArrayList<Particle> externalArchive ) {
        double min = Double.MAX_VALUE;
        
        for ( int i = 0; i < externalArchive.size(); i++ ) {
            if( externalArchive.get(i).getParticleData().getFitness()[k] < min ) {
                min = externalArchive.get(i).getParticleData().getFitness()[k];
            }
        }        
        return min;
    }    

    private void mapGridWithMappedCells(int nDimensions, int nGridByDimension) {
        int mappedCellsIndex = 0;
        
        int[] dimensionIndex = new int[nDimensions];  
        
        int currentDimension = 0;
        
        this.mapRecursive( mappedCellsIndex, currentDimension, nDimensions, nGridByDimension, dimensionIndex );        
    }

    private int mapRecursive( int mappedCellsIndex, int currentDimension, int nDimensions, int nGridByDimension, int[] dimensionIndex) {
        int currentCellGrid = 0;
        
        
        if ( currentDimension < nDimensions - 1 ) {
            
            while( currentCellGrid < nGridByDimension ) {
                dimensionIndex[currentDimension] = currentCellGrid;
                
                mappedCellsIndex = this.mapRecursive( mappedCellsIndex, currentDimension + 1, nDimensions, nGridByDimension, dimensionIndex );
                
                currentCellGrid++;
            }            
        }
        
        else {
            for ( int i = 0; i < nGridByDimension; i++ ) {
                
                for ( int j = 0; j < nDimensions - 1; j++ ) {
                    getMappedCells()[mappedCellsIndex].setLineGridMapped( j, dimensionIndex[j] );
                }                
                getMappedCells()[mappedCellsIndex].setLineGridMapped( nDimensions - 1, i );

                mappedCellsIndex++;
            }            
        }        
        return mappedCellsIndex;
    }
    

    private int calculateSolutionCellInDimension(double dimensionFitnessValue, int nGridByDimension, double minValue, double maxValue ) {

        int cellIndex;
        
        if( dimensionFitnessValue != maxValue ) {
            cellIndex = (int) ( ( dimensionFitnessValue - minValue ) / ( (maxValue - minValue) / nGridByDimension ) );
        }
        
        else {
            cellIndex = nGridByDimension - 1;
        }        
        
        return cellIndex;
    }

    private boolean incrementMappedCell( int[] dimensionIndex ) {
        
        for ( int i = 0; i < getMappedCells().length; i++ ) {
            if( Arrays.equals( getMappedCells()[i].getLineGridMapped(), dimensionIndex  ) ) {
                getMappedCells()[i].incrementNSolutionsInCell();
                return true;
            }            
        }        
        return false;
    }

    public int getNGridByDimension() {
        return nGridByDimension;
    }

    public void setNGridByDimension(int nGridByDimension) {
        this.nGridByDimension = nGridByDimension;
    }

    public double[] getMaxValue() {
        return maxValue;
    }

    public void setMaxValue(double[] maxValue) {
        this.maxValue = maxValue;
    }

    public double[] getSizeRange() {
        return sizeRange;
    }

    public void setSizeRange(double[] sizeRange) {
        this.sizeRange = sizeRange;
    }

    public MappedCells[] getMappedCells() {
        return mappedCells;
    }

    public void setMappedCells(MappedCells[] mappedCells) {
        this.mappedCells = mappedCells;
    }

    public double[] getMinValue() {
        return minValue;
    }

    public void setMinValue(double[] minValue) {
        this.minValue = minValue;
    }
}
